/*
 * Copyright 2008, Freescale Semiconductor, Inc
 * Andy Fleming
 *
 * Based vaguely on the Linux code
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <config.h>
#include <common.h>
#include <command.h>
#include <mmc.h>
#include <part.h>
#include <malloc.h>
#include <linux/list.h>
#include <div64.h>
#include "comman.h"
#include <oem_func.h>

int sd_send_op_cond(struct mmc *mmc)
{
	int timeout = 1000;                                                                  
	int err;                                                                             
	struct mmc_cmd cmd; 

	do {                                                                                
		cmd.cmdidx = MMC_CMD_APP_CMD;                                                
		cmd.resp_type = MMC_RSP_R1;                                                  
		cmd.cmdarg = 0;                                                              
		cmd.flags = 0;                                                               
                                                                                      
		err = mmc_send_cmd(mmc, &cmd, NULL);                                         
                                                                                      
		if (err)                                                                     
			return err;                                                          

		cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;                                        
		cmd.resp_type = MMC_RSP_R3;                                                 
		cmd.cmdarg = (mmc->voltages & 0xff8000);                                                  

		if (mmc->version == SD_VERSION_2)                                            
			cmd.cmdarg |= OCR_HCS;                                               
         	err = mmc_send_cmd(mmc, &cmd, NULL);

			if (err)                                                                     
				return err;                                                          
                                                                                      
			udelay(1000);                                                                
	} while ((!(cmd.response[0] & OCR_BUSY)) && timeout--);     
	
	if (timeout <= 0)                                                                    
		return UNUSABLE_ERR;                                                         
                                                                                        
	if (mmc->version != SD_VERSION_2)                                                   
		mmc->version = SD_VERSION_1_0;                                               
                                                                                      
	mmc->ocr = cmd.response[0];                                                          
                                                                                    
	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);                              
	mmc->rca = 0;  
	return 0;	
}

int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
{
	struct mmc_cmd cmd;
	struct mmc_data data;

	mode = !!mode;
	value &= 0xF;
	memset(&data, 0, sizeof(struct mmc_data));
	memset(&cmd, 0, sizeof(struct mmc_cmd));
	/* Switch the frequency */
	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
	cmd.cmdarg = (mode << 31) | 0x00FFFFFF;
	cmd.cmdarg &= ~(0xf << (group * 4));
	cmd.cmdarg |= value << (group * 4);
	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;

	data.dest = (char *)resp;
	data.blocksize = 64;
	data.blocks = 1;
	data.flags = MMC_DATA_READ;
	return mmc_send_cmd(mmc, &cmd, &data);
}
int sd_decode_csd(struct mmc *mmc)
{
	struct mmc_csd *csd = &(mmc->csd_data);
	unsigned int e, m, csd_struct;
	u32 *resp = mmc->csd;

	csd_struct = UNSTUFF_BITS(resp, 126, 2);
//	printf("csd_struct:%d\n",csd_struct);
	//int i=0;

	switch (csd_struct) {
		case 0:
//			m = UNSTUFF_BITS(resp, 115, 4);
//			e = UNSTUFF_BITS(resp, 112, 3);
//			csd->tacc_ns	 = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
//			printf("tacc_ns0:%d\n",csd->tacc_ns);
			csd->tacc_clks	 = UNSTUFF_BITS(resp, 104, 8) * 100;

//			m = UNSTUFF_BITS(resp, 99, 4);
//			e = UNSTUFF_BITS(resp, 96, 3);
			csd->max_dtr=25000000;
		//	csd->max_dtr  = mmc_calculate_dtr(m,e);
//			printf("Card transfer speed:%d  m:%d e:%d\n",csd->max_dtr ,m,e);
			csd->cmdclass	  = UNSTUFF_BITS(resp, 84, 12);

			e = UNSTUFF_BITS(resp, 47, 3);
			m = UNSTUFF_BITS(resp, 62, 12);
			mmc->sector_count  = (1 + m) << (e + 2);

			mmc->read_bl_len = UNSTUFF_BITS(resp, 80, 4);
			csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
			csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
			csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
			csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
			mmc->write_bl_len =1<< UNSTUFF_BITS(resp, 22, 4);
			csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
			break;
		case 1:
			/*
		 	* This is a block-addressed SDHC card. Most
		 	* interesting fields are unused and have fixed
		 	* values. To avoid getting tripped by buggy cards,
		 	* we assume those fixed values ourselves.
		 	*/
			mmc_card_set_blockaddr(mmc);

			csd->tacc_ns	 = 0; /* Unused */
			csd->tacc_clks	 = 0; /* Unused */

//			m = UNSTUFF_BITS(resp, 99, 4);
			e = UNSTUFF_BITS(resp, 96, 3);
//			printf("Card transfer speed:%d  m:%d e:%d\n",csd->max_dtr ,m,e);
//			csd->max_dtr  = mmc_calculate_dtr(m,e);
			csd->max_dtr  =25000000;
			csd->cmdclass	  = UNSTUFF_BITS(resp, 84, 12);
			m = UNSTUFF_BITS(resp, 48, 22);
			mmc->sector_count    = (1 + m) << 10;
		
			mmc->read_bl_len =9;
            csd->read_partial = 0;                                                        
            csd->write_misalign = 0;                                                      
            csd->read_misalign = 0;                                                       
            csd->r2w_factor = 4; /* Unused */                                             
            csd->write_partial = 0;                                                       
            break;                                                                        
       	default:                                                                              
          	printf("%s: unrecognised CSD structure version %d\n",                
                     	mmc->name, csd_struct);                                
            return -1;                                                               
  	}                                                                                     
	mmc->tran_speed=csd->max_dtr;
 	mmc->sector_count <<= mmc->read_bl_len-9;             	
	mmc->read_bl_len = 512;                                                        
	mmc->write_bl_len =512;                                                       
   	return 0;                                                                             
}

 /*
 * Fetches and decodes switch information
 */
int sd_read_switch(struct mmc *card)
{
	int err;
	u8 *status;
	
	if (card->sd_version < SCR_SPEC_VER_1){
		printf("SD version not surport high speed\n");
		return 0;
	}

	if (!(card->csd_data.cmdclass & CCC_SWITCH)) {
		printf("%s: card lacks mandatory switch "
			"function, performance might suffer.\n",
			card->name);
		return 0;
	}

	err = -1;

	status =malloc(64);
	if (!status) {
		printf("%s: could not allocate a buffer for "
			"switch capabilities.\n", card->name);
		return -1;
	}

	err = sd_switch(card, 0, 0, 1, status);
	if (err) {
		/* If the host or the card can't do the switch,
		 * fail more gracefully. */

		printf("%s: problem reading switch "
			"capabilities, performance might suffer.\n",
			card->name);
		goto out;
	}

	if (status[13] & 0x02)
		card->hs_max_dtr = 50000000;

//	printf("card->hs_max_dtr:%d\n",card->hs_max_dtr);

out:
	free(status);
	return 0;

}
void mmc_set_timing(struct mmc *mmc, unsigned int timing)
{              
   	mmc->clock = timing;
   	mmc_set_ios(mmc);
}

/*
 * Test if the card supports high-speed mode and, if so, switch to it.
 */
int sd_switch_hs(struct mmc *card)
{
	int err;
	u8 *status;

	if (card->sd_version < SCR_SPEC_VER_1)
	{
		printf("card->sd_version < SCR_SPEC_VER_1\n");
		return 0;
	}

	if (!(card->csd_data.cmdclass & CCC_SWITCH))
	{
		printf("card->csd_data.cmdclass & CCC_SWITCH\n");
		return 0;
	}

	if (!(card->host_caps & MMC_CAP_SD_HIGHSPEED)){
		printf("Card %s does not surport high speed\n",card->name);
		return 0;
	}

	if (card->hs_max_dtr == 0)
		return 0;

	err = -1;

	status = malloc(64);
	if (!status) {
		printf("%s: could not allocate a buffer for "
			"switch capabilities.\n", card->name);
		return -1;
	}

	err = sd_switch(card, 1, 0, 1, status);
	if (err){
		printf("failed switch sd high speed\n");
		goto out;
	}

	if ((status[16] & 0xF) != 1) {
		printf("%s: Problem switching card "
			"into high-speed mode!\n",
			card->name);
	} else {
		printf("set card high speed\n");
		mmc_card_set_highspeed(card);
		mmc_set_ios(card);
//		mmc_set_timing(card, MMC_TIMING_SD_HS);
	}

out:
	free(status);

	return err;
}
 /*                                                                                   
  * Compute bus speed.                                                                
  */
int sd_compute_bus_speed(struct mmc* mmc)
{
	unsigned int max_dtr;
  	max_dtr = (unsigned int)-1;                                                          
                                                                                             
 	if (mmc_card_highspeed(mmc)) {      
		//printf("High speed SD card:%x\n",mmc->hs_max_dtr);
               return mmc->hs_max_dtr;                                  
      	} 
	return  mmc->tran_speed;                                                           
}
static int sd_send_relative_addr(struct mmc *mmc)
{                                                                                             
       struct mmc_cmd cmd;                                                                   
                                                                                          
       cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;                                               
       cmd.cmdarg = mmc->rca << 16;                                                          
       cmd.resp_type = MMC_RSP_R6;                                                           
       cmd.flags = 0;                                                                        
                                                                                          
      if( mmc_send_cmd(mmc, &cmd, NULL)) return -1;                                         
                                                                                          
        mmc->rca = (cmd.response[0] >> 16) & 0xffff;                                          
      return 0;                                                                             
                                                                                           
}  
static int sd_decode_scr(struct mmc *card)
{
  	unsigned int scr_struct;                                                              
        u32 resp[4];                                                                          
                                                                                            
        resp[3] = card->raw_scr[1];                                                           
        resp[2] = card->raw_scr[0];                                                           
                                                                                               
//	printf("card->raw_scr[0]:0x%8x\n"
//			"card->raw_scr[1]:0x%8x\n ",card->raw_scr[0],card->raw_scr[1]);
      scr_struct = UNSTUFF_BITS(resp,60, 4); 
  //    scr_struct=card->raw_scr[0]&0x0000000F;                                              
        if (scr_struct != 0) {                                                                
        	printf("%s: unrecognised SCR structure version %d\n",                
                      card->name, scr_struct);                                
             return -1;                                                               
    	}                                                                                     
                                                                                             
     	card->sd_version = UNSTUFF_BITS(resp,56, 4); 
      switch(card->sd_version){
	      case 0:
		      card->sd_version=SD_VERSION_1_0;
		      printf("It is a version 1.0 sd card\n"); 
		      break;
	      case 1:
		      card->sd_version=SD_VERSION_1_10;
		      printf("It is a version 1.1 sd card\n"); 
		      break;
              case 2:
		      card->sd_version=SD_VERSION_2;
		      printf("It is a version 2.0 sd card\n");
		      break;
	      default:
		      printf("Unrecognised sd card, version number:%d\n",card->sd_version);
		      break;

      }	
      	card->bus_width  = 0x0F&UNSTUFF_BITS(resp, 48, 4);
	if(card->bus_width&0x4) card->card_caps |= MMC_MODE_4BIT;//card surport 4 bit buswith	
        return 0;
}
static int sd_get_version(struct mmc *card)
{
	int err;                                                                              
        struct mmc_cmd cmd;                                                               
        struct mmc_data data;                                                                 
        int* scr=(int*)card->raw_scr;

                                                                                          
        /* NOTE: caller guarantees scr is heap-allocated */                                   
                                                                                     
        //err = mmc_app_cmd(card->host, card);      
	 cmd.cmdidx = MMC_CMD_APP_CMD;//put card into app cmd state
         //cmd.resp_type = MMC_RSP_R1;
         cmd.cmdarg = card->rca << 16;

	 //cmd.arg = card->rca << 16;                                                    
	 cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
         err = mmc_send_cmd(card, &cmd, NULL);
         if (err)                                                                              
        	return err;                                                                   
                                                                                         
        memset(&cmd, 0, sizeof(struct mmc_cmd));                                          
        memset(&data, 0, sizeof(struct mmc_data));                                            
                                                                                              
                                                                                              
       	cmd.cmdidx = SD_CMD_APP_SEND_SCR;                                                         
       	cmd.cmdarg  = 0; 
      	cmd.flags  = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;                               
                                                                                           
        data.blocksize = 8;                                                                       
        data.blocks = 1;                                                                      
        data.flags = MMC_DATA_READ;                                                           
	data.dest = (char*)card->raw_scr;

	err = mmc_send_cmd(card, &cmd, &data);                                               
       	if (err)                                                                        
            	return err;                                                             
                                     
        scr[0] = be32_to_cpu(scr[0]);                                                         
        scr[1] = be32_to_cpu(scr[1]);                                                         
                                                                                            
      	return sd_decode_scr(card);                                                                         	
 } 
static int sd_reset_card(struct mmc *mmc)
{
	int err;

	err = mmc_go_idle(mmc);                                                               
        if (err){                                                                             
                printf("Error init 2: mmc error 0x%x\n",err);                                 
                 return err;                                                                   
       	}                                                                                     
      /* Test for SD version 2 */                                                           
      	err = mmc_send_if_cond(mmc);                                                          
        /* Now try to get the SD card's operating condition */                                
       	err = sd_send_op_cond(mmc);

	return err;
}
int sd_startup(struct mmc *mmc)
{
	int err;
	
	sd_reset_card(mmc);
	/* Put the Card in Identify Mode */
	if(mmc_send_cid(mmc)) return -1;
	/*
	 * For SD cards, get the Relatvie Address.
	 * This also puts the cards into Standby State
	 */
	if(sd_send_relative_addr(mmc))return -1;
	/* Get the Card-Specific Data */
	if(block_send_csd(mmc)) return -1;
	sd_decode_csd(mmc);
	if(mmc_into_trans_mode(mmc)) return -1;

	if(sd_get_version(mmc)) printf("failed to get sd version and bus width\n");
	err= sd_read_switch(mmc);
	err=sd_switch_hs(mmc);
	int max_dtr=sd_compute_bus_speed(mmc);     
	//if(max_dtr<25000000||mmc->Wflag)max_dtr=25000000;	
	if(max_dtr<25000000)max_dtr=25000000;
#ifdef CONFIG_SD_RECOMMAND_FREQ
	if (CONFIG_SD_RECOMMAND_FREQ>max_dtr)
		 mmc_set_clock(mmc, max_dtr);
	else
		mmc_set_clock(mmc, CONFIG_SD_RECOMMAND_FREQ);
#else
	mmc_set_clock(mmc,max_dtr);
#endif

	if (mmc->card_caps & MMC_MODE_4BIT) {                                                
    		if(mmc_app_set_bus_width(mmc, 4)){                                            
                	printf("HiGH-SPEED MODE NOT AVILABLE FOR YOUR CARD:%s!\n",mmc->name); 
                    	mmc_set_clock(mmc,0);                                                 
                    	if(mmc_app_set_bus_width(mmc, 4)){                                    
                       		max_dtr = mmc->tran_speed;                                    
                              	mmc_set_clock(mmc, max_dtr);                                  
                      		if(mmc_app_set_bus_width(mmc, MMC_BUS_WIDTH_4)){              
                                	printf("I don't known why!!\n");                      
                            		return -1;                                            
                    		}                                                             
                        }                                                                     
          	}                                                                             
           	mmc_set_bus_width(mmc,4);                                                     
      	}
	/* fill in device description */
	mmc_init_devdesc(mmc);
	err = mmc_set_blocklen(mmc, mmc->read_bl_len);
	if(err){
		printf("[u-boot]-sdhci:set block length failed.\n");
		return err;
	}
//	printf("[u-boot]-sdhci: MMC init completed.\n");
	mmc->initflag=MMC_INITIATED;
	return 0;
}
